#!/usr/bin/env python

import sys
import re
import os
import subprocess

QUEUE = 3
EVENT = 4

libs = []
maps = []
maps2 = []
addresses = []
ratags = []

addresscounts = []
addressminszs = []
addressmaxszs = []

raresults = []
ras = []

def parse_range(astr):
    result = set()
    for part in astr.split(','):
        x = part.split('-')
        result.update(range(int(x[0]), int(x[-1]) + 1))
    return sorted(result)


def getlibandaddr2line(address, tag):

   result = 'lib?|function?'
   for map in maps2:
      maparea = (re.search(r'[0-9a-f]+-[0-9a-f]+', map)).group()
      mapareas = maparea.split('-')

      if int(address, 16) >= int(mapareas[0], 16) and int(address, 16) < int(mapareas[1], 16):

         for lib in libs:

            if os.path.basename(map.split()[5]) in lib:
               if 'shared object' in subprocess.check_output('file ' + lib, shell = True):

                  cmd = 'addr2line -s -f -p -C' + ' -e ' + lib + ' -a ' + hex(int(address, 16) - int(mapareas[0], 16))
                  #sys.stderr.write('cmd=%s\n' % cmd)

                  addr2line = subprocess.check_output(cmd, shell = True).replace('\n', '').split(': ')[1]

                  return os.path.basename(map.split()[5]) + '|' + addr2line
                  break
         break

   return result

def sortby(x):
   return len(x.split()[0]), x.split()[0].lower(), int(x.split()[1][1:])

def main():

   if len(sys.argv) >= 4:

      #build libs
      for root, dirs, files in os.walk(sys.argv[3]):
         for file in files:
            if '.so' in file:
               libs.append(root + '/' + file)
      sys.stderr.write('libs done\n')

      #build maps
      #74955000-7495c000 rw-p 00151000 00:10 56755590   /usr/lib/libQt5Network.so.5.3.2
      f = open(sys.argv[2])
      for line in f:
         if '.so' in line:
            maps.append(line)
      f.close()
      sys.stderr.write('maps done\n')
      #for m in maps:
      #   sys.stderr.write('maps=[%s]\n' %m)

      f = open(sys.argv[1])

      #build maps2
      #DSC 0 0 MAPS,741b1000-74213000,r-xp,00000000,00:10,56754894,,,/lib/libuClibc-0.9.32.1.so
      for line in f:
         result = re.match(r'DSC 0 0 MAPS,[0-9a-f]{8}', line)
         if result:
            l = line[13:].replace(',',' ')
            if l not in maps2:
               maps2.append(l)
      #sys.stderr.write('maps2 done\n')

      f.seek(0)

      if len(sys.argv) >= 5:
         cyclerange = parse_range(sys.argv[6])
      else:
         cyclerange = parse_range('1')


      cycle = 0
      allow = False

      for line in f:

         if len(sys.argv) >= 5:

            if line.startswith('DSC'):
               if re.search(sys.argv[4], line):
                  cycle += 1
                  allow = True
               elif re.search(sys.argv[5], line):
                  allow = False

         else:
            allow = True
            cycle = 1

         if allow:

            if cycle in cyclerange:

               if line.startswith('DSC'):
                  #raresult = re.search(r'(r|m),=[0-9a-f]+,ra=[0-9a-f]+,sz=[0-9]+', line)
                  raresult = re.search(r',=[0-9a-f]+,ra=[0-9a-f]+,sz=[0-9]+', line)
                  if raresult:

                     malloc = (raresult.group().split(',')[1])[1:]
                     address = (raresult.group().split(',')[2])[3:]
                     size = int((raresult.group().split(',')[3])[3:])
                     ratag = line.split(' ')[3][:-1]

                     if not address in addresses:
                        addresses.append(address)
                        addresscounts.append(1)
                        addressminszs.append(size)
                        addressmaxszs.append(size)

                        ratags.append(ratag)
                        addr2line = getlibandaddr2line(address, ratag)
                        raresults.append(address + '|' + addr2line)

                     else:
                        addresscounts[addresses.index(address)] += 1

                        if size > addressmaxszs[addresses.index(address)]:
                           addressmaxszs[addresses.index(address)] = size

                        if size < addressminszs[addresses.index(address)]:
                           addressminszs[addresses.index(address)] = size


      i = 0
      for item in raresults:
         lib = raresults[i].split('|')[1]
         line = raresults[i].split('|')[2]
         text = raresults[i].split('|')[0] + ',' + lib.split('.so')[0] +':' + line.split('at ')[0].replace(' ', '_')
         if len(text) > 152:
            text = text[:150] + '..'
         ras.append(text)

         text = "%s #%-6d %7d..%-7d %s:%s" % ((ratags[i].split(',')[0]).ljust(44) , addresscounts[i], addressminszs[i], addressmaxszs[i],
                        lib.split('.so')[0], line.split('at ')[0])
         if len(text) > 182:
            text = text[:180] + '..'

         raresults[i] = text;
         i+= 1

      raresults.sort(key = sortby)

      for raresult in raresults:
         sys.stderr.write('%s\n' % raresult)




      #
      eventnams = []
      queuenams = []
      prev_malloc = []

      f.seek(0)
      for line in f:

         if line.startswith('OCC') or line.startswith('STA') or line.startswith('STO'):
            last_tstamp = line.split()[3]

         if line.startswith('DSC'):

            result = re.search(r'ra=[0-9a-f]+,sz=[0-9]+', line)
            if result:
               #prepend with ra data
               for ra in ras:
                  if ra.split(',')[0] in result.group():
                     print 'DSC 0 0 %s:%s' %((ra.split(',')[1])[:-1], line.split(' ')[3]),
                     break

            else:
               print line,



            #build other data
            if result:

               for ra in ras:
                  if ra.split(',')[0] in result.group():

                     #sys.stderr.write('%s\n' % ra)
                     malloc = int((result.group().split(',')[1])[3:])

                     if not ra in eventnams:
                        print 'NAM %d %d %s' % (EVENT, EVENT * 10000 + len(eventnams), ra)
                        eventnams.append(ra)

                        print 'NAM %d %d %s' % (QUEUE, QUEUE * 11000 + len(queuenams), ra)
                        queuenams.append(ra)

                        prev_malloc.append(0)

                     print 'OCC ' + str(EVENT) + ' %d %s' %((EVENT * 10000 + queuenams.index(ra), last_tstamp))
                     print 'DSC 0 0 %d' % malloc

                     if prev_malloc[queuenams.index(ra)]:
                        print 'STO %d %d %s %d' % (QUEUE, QUEUE * 11000 + queuenams.index(ra), last_tstamp, prev_malloc[queuenams.index(ra)])
                     print 'STA %d %d %s %d' % (QUEUE, QUEUE * 11000 + queuenams.index(ra), last_tstamp, malloc)
                     prev_malloc[queuenams.index(ra)] = malloc
                     break

         else:
            print line,

   else:

      print("Usage: %s tdifile mapsfile pathtorootfs [cyclepattern-start cyclepattern-end cyclerange]" % sys.argv[0])

if __name__ == "__main__":
   main()
